package com.wangjiacai.utils.wav;

import com.wangjiacai.utils.utils.BytesUtil;
import lombok.Data;

import javax.sound.sampled.AudioSystem;
import javax.sound.sampled.UnsupportedAudioFileException;
import java.io.*;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.TimeUnit;


@Data
public class Wav {

    public static int AudioFormat = 1;
    public static int NumChannels = 1;

    private byte[] chunkId = "RIFF".getBytes();
    private byte[] chunkSize;
    private byte[] format = "WAVE".getBytes();

    private byte[] fmtId = "fmt ".getBytes();
    private byte[] fmtSize = BytesUtil.getLittleBytes(16, 4);
    private byte[] audioFormat = new byte[2];
    private byte[] numChannel = new byte[2];
    private byte[] sampleRate;
    private byte[] byteRate;
    private byte[] blockAlign;
    private byte[] bitsPerSample;

    private byte[] dataId = "data".getBytes();
    private byte[] dataSize;

    private int dataLength;
    private List<byte[]> data = new ArrayList<>();
    private SampleRateEnum sampleRateEnum;
    private NumChannelEnum numChannelEnum;
    private BitPerSampleEnum bitPerSampleEnum;


    public Wav() {
        this(AudioFormatEnum.PCM, NumChannelEnum.One, SampleRateEnum.SAMPLE_RATE_16K, BitPerSampleEnum.BitPerSample16B);
    }

    public Wav(NumChannelEnum numChannelEnum) {
        this(AudioFormatEnum.PCM, numChannelEnum, SampleRateEnum.SAMPLE_RATE_16K, BitPerSampleEnum.BitPerSample16B);
    }

    public Wav(NumChannelEnum numChannelEnum, BitPerSampleEnum bitPerSampleEnum) {
        this(AudioFormatEnum.PCM, numChannelEnum, SampleRateEnum.SAMPLE_RATE_16K, bitPerSampleEnum);
    }

    public Wav(NumChannelEnum numChannelEnum, SampleRateEnum sampleRateEnum, BitPerSampleEnum bitPerSampleEnum) {
        this(AudioFormatEnum.PCM, numChannelEnum, sampleRateEnum, bitPerSampleEnum);
    }

    public Wav(AudioFormatEnum audioFormatEnum, NumChannelEnum numChannelEnum, SampleRateEnum sampleRateEnum, BitPerSampleEnum bitPerSampleEnum) {
        this.sampleRateEnum = sampleRateEnum;
        this.numChannelEnum = numChannelEnum;
        this.bitPerSampleEnum = bitPerSampleEnum;
        this.audioFormat[0] = audioFormatEnum.getValue();
        this.numChannel[0] = numChannelEnum.getValue();
        this.sampleRate = BytesUtil.getLittleBytes(sampleRateEnum.getValue(), 4);
        this.bitsPerSample = BytesUtil.getLittleBytes(bitPerSampleEnum.getValue(), 2);
        this.blockAlign = BytesUtil.getLittleBytes(numChannelEnum.getValue() * bitPerSampleEnum.getValue() / 8, 2);
        this.byteRate = BytesUtil.getLittleBytes(numChannelEnum.getValue() * sampleRateEnum.getValue() * bitPerSampleEnum.getValue() / 8, 4);
    }

    public void appendData(byte[] bytes) {
        data.add(bytes);
        this.dataLength += bytes.length;
    }

    public void relay(int num, TimeUnit timeUnit) {
        long seconds = timeUnit.toSeconds(num);
        int size = (int) seconds * numChannelEnum.getValue() * sampleRateEnum.getValue() * bitPerSampleEnum.getValue() / 8;
        byte[] bytes = new byte[size];
        this.appendData(bytes);
    }

    public byte[] writeToWavBytes() {
        byte[] bytes = new byte[this.dataLength + 44];
        this.dataSize = BytesUtil.getLittleBytes(this.dataLength, 4);
        this.chunkSize = BytesUtil.getLittleBytes(this.dataLength + 36, 4);
        int position = 0;
        System.arraycopy(this.chunkId, 0, bytes, position, this.chunkId.length);
        position += this.chunkId.length;
        System.arraycopy(this.chunkSize, 0, bytes, position, this.chunkSize.length);
        position += this.chunkSize.length;
        System.arraycopy(this.format, 0, bytes, position, this.format.length);
        position += this.format.length;

        System.arraycopy(this.fmtId, 0, bytes, position, this.fmtId.length);
        position += this.fmtId.length;
        System.arraycopy(this.fmtSize, 0, bytes, position, this.fmtSize.length);
        position += this.fmtSize.length;
        System.arraycopy(this.audioFormat, 0, bytes, position, this.audioFormat.length);
        position += this.audioFormat.length;
        System.arraycopy(this.numChannel, 0, bytes, position, this.numChannel.length);
        position += this.numChannel.length;
        System.arraycopy(this.sampleRate, 0, bytes, position, this.sampleRate.length);
        position += this.sampleRate.length;
        System.arraycopy(this.byteRate, 0, bytes, position, this.byteRate.length);
        position += this.byteRate.length;
        System.arraycopy(this.blockAlign, 0, bytes, position, this.blockAlign.length);
        position += this.blockAlign.length;
        System.arraycopy(this.bitsPerSample, 0, bytes, position, this.bitsPerSample.length);
        position += this.bitsPerSample.length;
        System.arraycopy(this.dataId, 0, bytes, position, this.dataId.length);
        position += this.dataId.length;
        System.arraycopy(this.dataSize, 0, bytes, position, this.dataSize.length);
        position += this.dataSize.length;
        byte[] dataBytes = BytesUtil.getAllBytes(this.data);
        System.arraycopy(dataBytes, 0, bytes, position, dataBytes.length);
        return bytes;
    }

    public InputStream writeToWavInputStream() {
        return new ByteArrayInputStream(this.writeToWavBytes());
    }

}
